package com.alex.controller.Cn_Pubmed;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.alex.common.config.IndexConfig;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.IOException;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

public class ResearchHotspots {
    /**
     * 仿科研者之家的中文PubMed的研究热点这一部分。主方法：getResearchHotspotsInfo，必传的参数为论文基础表：pm_ut_aid_info和检索结果的aid号的集合，若检索无结果，返回为空。
     * 贾新志 2024.03.24
     */


    public static List<KeyWordSumAids> researchHotspots(List<Integer> ids) throws SQLException, IOException {

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(format.format(new Date()) + " 程序开始了~~~");

        String pmUtAidInfoTable = IndexConfig.db_article;
        // 以下为模拟获取数量，仅在测试时使用。
        System.out.println("pmOrUtIdList.size(): " + ids.size());
        /**
         * getResearchHotspotsInfo为主方法，参数，pmOrUtIdList：表pm_ut_aid_info自增主键的集合，即ES检索的结果。pmUtAidInfoTable：表名。
         * 参数ab1OrTi3OrKey5OrAll6:1为基于摘要，3为基于标题，5为基于关键词，6为基于综合（摘要、标题、关键词，此时需要填写系数，默认摘要为1，标题为5，关键词为9，系数为int类型，大于0小于100），默认为5。
         * 参数year0OrZky2OrNum4：0为基于出版年，2为基于中科院期刊分区，4为基于词词频，默认为4。
         */
        ResearchHotspotsInfo researchHotspotsInfo = getResearchHotspotsInfo(ids, pmUtAidInfoTable, 6, 4, 1, 5, 9);
        int max = researchHotspotsInfo.getMax();
        System.out.println("最大的关键词的论文数量，作为柱状图的最大值: " + max);
        List<KeyWordSumAids> keyWordAidList = researchHotspotsInfo.getKeyWordAidList();
        System.out.println(format.format(new Date()) + " 程序结束了~~");
        List<KeyWordSumAids> finalList = new ArrayList<>();
        for (KeyWordSumAids keyWordSumAids : keyWordAidList) {
            try {
                String s = keyWordSumAids.getKeyWord();
                int sum = keyWordSumAids.getSum();
                List<Integer> aidList = keyWordSumAids.getAidList();
                List<String> list = JSONObject.parseArray(s, String.class);
                String s2 = list.get(0);
                String s1 = filterString(s2);
                if (StrUtil.isNotEmpty(s1)) {
                    finalList.add(new KeyWordSumAids(aidList, sum, s1));
                }
            } catch (Exception e) {
                e.printStackTrace(System.err);
            }
        }
        return finalList;
    }

    public static String filterString(String s) {
        // 定义需要检查的前缀列表
        String[] prefixes = {"the ", "and ", "in ", "of ", "at ", "to ", "on ", "this ", "from ", "and", "the", "for"};
        // 定义需要检查的后缀列表
        String[] suffixes = {"the ", "and ", "in ", "of ", "at ", "to ", "on ", "this ", "from ", "and", "the", "for"};

        // 检查是否以指定的前缀开头
        for (String prefix : prefixes) {
            if (s.startsWith(prefix)) {
                return null; // 不符合要求，返回null
            }
        }

        // 检查是否以指定的后缀结尾
        for (String suffix : suffixes) {
            if (s.endsWith(suffix)) {
                return null; // 不符合要求，返回null
            }
        }

        // 检查是否有单个字符前后有空格的情况
        Pattern pattern = Pattern.compile("^[a-zA-Z0-9] .+?$|.+? [a-zA-Z0-9]$");
        if (pattern.matcher(s).find()) {
            return null; // 不符合要求，返回null
        }

        // 如果所有条件都不满足，则返回原字符串
        return s;
    }


    // 参数ab1OrTi3OrKey5OrAll6:1为基于摘要，3为基于标题，5为基于关键词，6为基于综合（摘要、标题、关键词，此时需要填写系数，默认摘要为1，标题为5，关键词为9，系数为int类型，大于0小于100）。参数year0OrZky2OrNum4：0为基于出版年，2为基于中科院期刊分区，4为基于词词频。
    private static ResearchHotspotsInfo getResearchHotspotsInfo(List<Integer> pmOrUtIdList, String pmUtAidInfoTable, int ab1OrTi3OrKey5OrAll6, int year0OrZky2OrNum4, int abFactor, int tiFactor, int keyFactor) throws SQLException {
        if (null == pmOrUtIdList || pmOrUtIdList.isEmpty()) {
            return new ResearchHotspotsInfo();
        }
        int nowYear = getNowYear();
        List<Entity> tableData = Db.use().query("select `ti_key`,`id`,`year`,`zky_dalei`,`ab_key`,`kw_key` from " + pmUtAidInfoTable + " where id in(" + CollUtil.join(new HashSet<>(pmOrUtIdList), ",") + ");");
        Map<String, String> lowerCaseAndUpperCaseMap = new HashMap<>();
        Map<String, Double> keyWordAndScoreMap = new HashMap<>();
        Map<String, Set<Integer>> keyWordAndAidSetMap = new HashMap<>();
        Set<String> bracketKeyWordSet = new HashSet<>();
        for (Entity entry : tableData) {
            Integer id = entry.getInt("id");
            Integer year = entry.getInt("year");
            Integer zkyDalei = entry.getInt("zky_dalei");
            year = (null == year) ? 1900 : year;
            zkyDalei = (null == zkyDalei) ? 0 : zkyDalei;
            // 参数year0OrZky2OrNum4：0为基于出版年，2为基于中科院期刊分区，4为基于词词频，默认为基于词频。
            double basis = 1.0;
            if (year0OrZky2OrNum4 == 0) {
                basis = 0.2;
                basis = (nowYear == year) ? 1.0 : basis;
                basis = (nowYear - 1 == year) ? 0.85 : basis;
                basis = (nowYear - 2 == year) ? 0.7 : basis;
                basis = (nowYear - 3 == year) ? 0.55 : basis;
                basis = (nowYear - 4 == year) ? 0.4 : basis;
            }
            if (year0OrZky2OrNum4 == 2) {
                basis = 0.1;
                basis = (zkyDalei == 1) ? 1.0 : basis;
                basis = (zkyDalei == 2) ? 0.5 : basis;
                basis = (zkyDalei == 3) ? 0.33 : basis;
                basis = (zkyDalei == 4) ? 0.25 : basis;
            }

            // 参数ab1OrTi3OrKey5OrAll6:1为基于摘要，3为基于标题，5为基于关键词，6为基于综合（摘要、标题、关键词，此时需要设置系数，默认摘要为1，标题为5，关键词为9，系数为int类型，大于0小于100），默认为基于关键词。
            if (ab1OrTi3OrKey5OrAll6 == 6) {
                putKeyWordAndScoreMap(bracketKeyWordSet, keyWordAndScoreMap, lowerCaseAndUpperCaseMap, entry.getStr("ab_key"), keyWordAndAidSetMap, id, basis, abFactor);
                putKeyWordAndScoreMap(bracketKeyWordSet, keyWordAndScoreMap, lowerCaseAndUpperCaseMap, entry.getStr("ti_key"), keyWordAndAidSetMap, id, basis, tiFactor);
                putKeyWordAndScoreMap(bracketKeyWordSet, keyWordAndScoreMap, lowerCaseAndUpperCaseMap, entry.getStr("kw_key"), keyWordAndAidSetMap, id, basis, keyFactor);
            } else {
                String field = "kw_key";
                field = (ab1OrTi3OrKey5OrAll6 == 1) ? "ab_key" : field;
                field = (ab1OrTi3OrKey5OrAll6 == 3) ? "ti_key" : field;
                putKeyWordAndScoreMap(bracketKeyWordSet, keyWordAndScoreMap, lowerCaseAndUpperCaseMap, entry.getStr(field), keyWordAndAidSetMap, id, basis, 1);
            }
        }

        // 合并关键词，包括四个方面的合并，一是带括号，二是两个词，三是三个词中间有介词，四是三个词包含前两个或后两个。使用这个方法，会出现表面上应该包括某个词组，但点击该篇论文后，看不到这个词组（可能只包含其中的一个词），此问题若严重，下面这一行代码可以注掉。
        mergeKeyWord(bracketKeyWordSet, keyWordAndScoreMap, keyWordAndAidSetMap);
        Map<String, Double> sortValueDouLen = mapSortValueDouLen(keyWordAndScoreMap, 20);
        Map<String, List<Integer>> keyWordAndAidListMap = new HashMap<>();
        Map<String, Integer> keyWordAndAidSumMap = new HashMap<>();
        // 前端不显示各关键词的得分，而是显示自增主键aid集合的数量，因此，需要将最大得分前20个的，转换为aid集合（keyWordAndAidListMap）和集合的数量（keyWordAndAidSumMap）。
        for (Map.Entry<String, Double> entry : sortValueDouLen.entrySet()) {
            if (keyWordAndAidSetMap.containsKey(entry.getKey())) {
                keyWordAndAidListMap.put(entry.getKey(), new ArrayList<>(keyWordAndAidSetMap.get(entry.getKey())));
                keyWordAndAidSumMap.put(entry.getKey(), keyWordAndAidSetMap.get(entry.getKey()).size());
            }
        }

        Map<String, Integer> sortValueStrInt = mapSortValueStrInt(keyWordAndAidSumMap);
        List<KeyWordSumAids> keyWordAndAidList = new ArrayList<>();
        int max = 0;
        // 对集合的数量排序，作为最终结果的输出，先输出各关键词、自增主键的集合、集合的数量，保存进keyWordAndAidList，并计算最大的集合的数量，作为柱状图的最大值。
        for (Map.Entry<String, Integer> entry : sortValueStrInt.entrySet()) {
            String keyWord = entry.getKey();
            if (keyWordAndAidListMap.containsKey(keyWord)) {
                List<Integer> aidList = keyWordAndAidListMap.get(keyWord);
                KeyWordSumAids keyWordAidList = new KeyWordSumAids();
                int size = aidList.size();
                max = Math.max(size, max);
                keyWordAidList.setAidList(aidList);
                keyWordAidList.setSum(size);
                keyWordAidList.setKeyWord(lowerCaseAndUpperCaseMap.getOrDefault(keyWord, keyWord));
                keyWordAndAidList.add(keyWordAidList);
            }
        }
        // 输出给前端，包含集合的最大值，以及各关键词的相关信息（aid集合、集合的数量、关键词的名称）
        ResearchHotspotsInfo researchHotspotsInfo = new ResearchHotspotsInfo();
        researchHotspotsInfo.setMax(max);
        researchHotspotsInfo.setKeyWordAidList(keyWordAndAidList);
        return researchHotspotsInfo;
    }

    // put关键词，参数，bracketKeyWordSet：带括号或二位三位词组的集合；keyWordAndScoreMap：各关键词的得分；lowerCaseAndUpperCaseMap：小写与大写关键词的对应关系；keyWords：关键词的字符串集合；keyWordAndAidSetMap，各关键词的aid号集合；id：表的自增主键；basis：基本的分值；factor：系数。
    public static void putKeyWordAndScoreMap(Set<String> bracketKeyWordSet, Map<String, Double> keyWordAndScoreMap, Map<String, String> lowerCaseAndUpperCaseMap, String keyWords,
                                             Map<String, Set<Integer>> keyWordAndAidSetMap, Integer id, double basis, int factor) {
        if (null != keyWords && null != id && keyWords.contains("]") && id > 0) {
            try {
                List<String> keyWordList = JSONObject.parseArray(keyWords, String.class);
                for (String keyWord : keyWordList) {
                    String lowerCase = keyWord.toLowerCase();
                    if (Pattern.compile("([A-Z])").matcher(keyWord).find()) {
                        lowerCaseAndUpperCaseMap.put(lowerCase, keyWord);
                    }
                    if (lowerCase.endsWith(")") && lowerCase.contains("(")) {
                        String front = lowerCase.substring(0, lowerCase.lastIndexOf("(")).trim();
                        String end = lowerCase.substring(lowerCase.lastIndexOf("(") + 1, lowerCase.length() - 1).trim();
                        // 带括号的词组，比如：hepatocyte growth factor (HGF)，如果全写与简写的有关系，则加入到带括号的词组的集合内。
                        if (front.length() > 5 && end.length() > 2 && fullAndAbbrEquals(front, end)) {
                            bracketKeyWordSet.add(lowerCase);
                        }
                    }
                    String[] split = lowerCase.split(" ");
                    if (split.length == 2 || split.length == 3) {
                        bracketKeyWordSet.add(lowerCase);
                    }
                    keyWordAndScoreMap.compute(lowerCase, (key, value) -> value != null ? (value + (basis * factor)) : (basis * factor));
                    putStrSetInt(lowerCase, id, keyWordAndAidSetMap);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 如果某关键词与另一个关键词，需要合并，那么，先把原来的集合拿到，再把待合并的给合并进去，最后再更新原来的map值。
    public static void mergeAidSet(Map<String, Set<Integer>> keyWordAndAidSetMap, String keyWord, String merge) {
        if (keyWordAndAidSetMap.containsKey(keyWord)) {
            Set<Integer> aidSet = keyWordAndAidSetMap.get(keyWord);
            if (keyWordAndAidSetMap.containsKey(merge)) {
                aidSet.addAll(keyWordAndAidSetMap.get(merge));
            }
            keyWordAndAidSetMap.put(keyWord, aidSet);
        }
    }

    public static void mergeKeyWord(Set<String> bracketKeyWordSet, Map<String, Double> keyWordAndScoreMap, Map<String, Set<Integer>> keyWordAndAidSetMap) {
        // 对带括号的词组的合并，先将带括号的词组保存进bracketKeyWordSet循环，再将该词组分为前半部分和后半部分，如果keyWordAndScoreMap包含前半部分或后半部分，得分给予累加，并给予移除。
        for (String keyWord : bracketKeyWordSet) {
            if (keyWordAndScoreMap.containsKey(keyWord)) {
                if (keyWord.endsWith(")") && keyWord.contains("(")) {
                    String front = keyWord.substring(0, keyWord.lastIndexOf("(")).trim();
                    String end = keyWord.substring(keyWord.lastIndexOf("(") + 1, keyWord.length() - 1).trim();
                    if (fullAndAbbrEquals(front, end)) {
                        Double score = keyWordAndScoreMap.get(keyWord);
                        if (front.length() > 5 && keyWordAndScoreMap.containsKey(front)) {
                            score += keyWordAndScoreMap.get(front);
                            keyWordAndScoreMap.remove(front);
                            mergeAidSet(keyWordAndAidSetMap, keyWord, front);
                        }
                        if (end.length() > 2 && keyWordAndScoreMap.containsKey(end)) {
                            score += keyWordAndScoreMap.get(end);
                            keyWordAndScoreMap.remove(end);
                            mergeAidSet(keyWordAndAidSetMap, keyWord, end);
                        }
                        keyWordAndScoreMap.put(keyWord, score);
                    }
                }

                String[] split = keyWord.split(" ");
                // ["physicians","proposed","school","health","school health"] 处理这种的，集合中包含词组，还包含了各个词
                if (split.length == 2) {
                    if (keyWordAndScoreMap.containsKey(split[0]) && keyWordAndScoreMap.containsKey(split[1])) {
                        Double score = keyWordAndScoreMap.get(keyWord);
                        score += keyWordAndScoreMap.get(split[0]);
                        score += keyWordAndScoreMap.get(split[1]);
                        keyWordAndScoreMap.remove(split[0]);
                        mergeAidSet(keyWordAndAidSetMap, keyWord, split[0]);
                        keyWordAndScoreMap.remove(split[1]);
                        mergeAidSet(keyWordAndAidSetMap, keyWord, split[1]);
                        keyWordAndScoreMap.put(keyWord, score);
                    }
                }
                // ["EXPORT","QUEBEC","ELECTRICITY","EXPORT OF ELECTRICITY"] 处理这种的，词组中仅仅是多了一个介词，而另外两个词也在集合中的。或者前两个单词和后一个单词，或者前一个单词和后两个单词，都在集合中，也可以这样处理。
                if (split.length == 3) {
                    String l = split[1].toLowerCase();
                    if (keyWordAndScoreMap.containsKey(split[0]) && keyWordAndScoreMap.containsKey(split[2]) && ("at".equals(l) || "if".equals(l) || "of".equals(l) || "in".equals(l) || "on".equals(l) || "by".equals(l) ||
                            "to".equals(l) || "up".equals(l) || "a".equals(l) || "and".equals(l) || "it".equals(l) || "is".equals(l) || "an".equals(l))) {
                        Double score = keyWordAndScoreMap.get(keyWord);
                        score += keyWordAndScoreMap.get(split[0]);
                        score += keyWordAndScoreMap.get(split[2]);
                        keyWordAndScoreMap.remove(split[0]);
                        mergeAidSet(keyWordAndAidSetMap, keyWord, split[0]);
                        keyWordAndScoreMap.remove(split[2]);
                        mergeAidSet(keyWordAndAidSetMap, keyWord, split[2]);
                        keyWordAndScoreMap.put(keyWord, score);
                    }
                    if (keyWordAndScoreMap.containsKey(split[0] + " " + split[1]) && keyWordAndScoreMap.containsKey(split[2])) {
                        Double score = keyWordAndScoreMap.get(keyWord);
                        score += keyWordAndScoreMap.get(split[0] + " " + split[1]);
                        score += keyWordAndScoreMap.get(split[2]);
                        keyWordAndScoreMap.remove(split[0] + " " + split[1]);
                        mergeAidSet(keyWordAndAidSetMap, keyWord, split[0] + " " + split[1]);
                        keyWordAndScoreMap.remove(split[2]);
                        mergeAidSet(keyWordAndAidSetMap, keyWord, split[2]);
                        keyWordAndScoreMap.put(keyWord, score);
                    }
                    if (keyWordAndScoreMap.containsKey(split[1] + " " + split[2]) && keyWordAndScoreMap.containsKey(split[0])) {
                        Double score = keyWordAndScoreMap.get(keyWord);
                        score += keyWordAndScoreMap.get(split[1] + " " + split[2]);
                        score += keyWordAndScoreMap.get(split[0]);
                        keyWordAndScoreMap.remove(split[1] + " " + split[2]);
                        mergeAidSet(keyWordAndAidSetMap, keyWord, split[1] + " " + split[2]);
                        keyWordAndScoreMap.remove(split[0]);
                        mergeAidSet(keyWordAndAidSetMap, keyWord, split[0]);
                        keyWordAndScoreMap.put(keyWord, score);
                    }
                }
            }
        }
    }

    // 获取现在年，方法是当前时间戳减去8个月后再取年，即2021年7月1日是2021年，2021年6月1日是2020年。此用于新词发现，最长的比较就是一年半与之前的所有相比。
    public static int getNowYear() {
        long time = System.currentTimeMillis() - 60 * 60 * 24 * 30 * 6 * 1000L;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        String format = sdf.format(new Date(time));
        if (NumberUtil.isInteger(format)) {
            return Integer.parseInt(format);
        }
        return 1900;
    }

    public static void putStrSetInt(String str, Integer id, Map<String, Set<Integer>> strSetIntMap) {
        strSetIntMap.compute(str, (k, v) -> {
            if (v == null) {
                v = new ConcurrentHashSet<>();
            }
            v.add(id);
            return v;
        });
    }

    // 对Map的值进行排序，此排序为最大的在前面，输入一个map，再返回一个排序后的新map，String, Double类型，本排序增加了过滤功能，可以设置要前多少（len）个。
    public static Map<String, Double> mapSortValueDouLen(Map<String, Double> mapName, Integer len) {
        Map<String, Double> result = new LinkedHashMap<>();
        mapName.entrySet().stream().sorted(Map.Entry.<String, Double>comparingByValue().reversed()).limit(len).forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    // 对Map的值进行排序，此排序为最大的在前面，输入一个map，再返回一个排序后的新map，String, Integer类型。
    public static Map<String, Integer> mapSortValueStrInt(Map<String, Integer> mapName) {
        Map<String, Integer> result = new LinkedHashMap<>();
        mapName.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    // 判断词组与缩写的单词之间是否相等，如果词组内各单词的首字母与缩写的单词的各个首字母相等，或者首字母相等后半部分包含，可以认定为相等。比如：Mendelian randomization-Egger (MR-Egger)或hepatocyte growth factor (HGF)，要求全写的词组full必须包含空格，缩写的单词abbr必须大于1位，且首字母必须相同。
    public static boolean fullAndAbbrEquals(String full, String abbr) {
//        System.out.println("~~~full:" + full + " abbr:" + abbr);
        if (null != full && null != abbr && (full.trim().contains(" ") || full.contains("-") || full.length() > 11) && abbr.length() > 1 &&
                full.replace("(", "").replace(")", "").substring(0, 1).equalsIgnoreCase(abbr.substring(0, 1)) && full.length() >= abbr.length()) {
            if (full.equalsIgnoreCase(abbr)) {
                return true;
            }
            // 如果缩写不包含"-"，则可以把全写词组中的给予替换成空格，若包含，则不可以，比如：autosomal-dominant PKD(ADPKD)
            full = full.replace("-", " ").replace("(", "").replace(")", "").toLowerCase().replaceAll(" +", " ").trim();
            abbr = abbr.replace("-", "").toLowerCase().replaceAll(" +", " ").trim();
            String[] fullSplit = full.split(" ");
            String[] abbrSplit = abbr.split("");
            // 如果词组的个数与缩写单词的个数相同，且词组内各单词的首字母与缩写的单词的各个首字母相等，使用下面的方法。
            if (fullSplit.length == abbrSplit.length) {
                boolean bool = true;
                for (int i = 0; i < fullSplit.length; i++) {
                    if (!fullSplit[i].substring(0, 1).equals(abbrSplit[i])) {
                        bool = false;
                        break;
                    }
                }
                if (bool) {
                    return true;
                }
            }
            // full:nanosecond pulsed electric field abbr:nsPEF
            if (fullSplit.length == 4 && abbrSplit.length == 5) {
                if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].startsWith(abbrSplit[2])
                        && fullSplit[2].startsWith(abbrSplit[3]) && fullSplit[3].startsWith(abbrSplit[4])) {
                    return true;
                }
            }
            // full:pancreatic neuroendocrine tumors abbr:PanNETs
            if (fullSplit.length == 3 && abbr.length() > 3) {
                boolean bool = true;
                int sum = 0;
                T:
                for (int i = abbr.length(); i > 0; i--) {
                    if (fullSplit[0].startsWith(abbr.substring(0, i))) {
                        // 第一次，abbr是完整的，第二次时，abbr变成了nets
                        String second = abbr.substring(i);
                        for (int j = second.length(); j > 0; j--) {
                            if (fullSplit[1].startsWith(second.substring(0, j))) {
                                // 第三次时，abbr变成了ts
                                String third = second.substring(j);
                                String[] split = third.split("");
                                // 如果全部包含，则维持bool仍为true，否则有任一个不是则为false。为什么要加一个变量sum？因为bool的默认值是true，如果程序走不到这一步呢？这样bool仍为true，加上sum后，要求其必须执行++，就可以确保程序走到了这一步。
                                for (String s : split) {
                                    if (fullSplit[2].contains(s)) {
                                        sum++;
                                    } else {
                                        bool = false;
                                        break T;
                                    }
                                }
                                break T;
                            }
                        }
                    }
                }
                if (bool && sum > 0) {
                    return true;
                }
            }
            // chemotherapy (CT)
            if (fullSplit.length == 1 && abbrSplit.length == 2) {
                return fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]);
            }
            // full:lipopolysaccharide abbr:LPS
            if (fullSplit.length == 1 && abbrSplit.length == 3) {
                return fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) && fullSplit[0].contains(abbrSplit[2]);
            }
            // interquartile range (IQR)
            if (fullSplit.length == 2 && abbrSplit.length == 3) {
                if (fullSplit[0].startsWith(abbrSplit[0])) {
                    if (fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].startsWith(abbrSplit[2])) {
                        return true;
                    }
                    if (fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[2])) {
                        return true;
                    }
                }
            }
            // full:pseudorandom sinusoidal abbr:prss
            if (fullSplit.length == 2 && abbrSplit.length == 4) {
                if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[2])) {
                    if (fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[3])) {
                        return true;
                    }
                }
            }
            return isEquals(full, abbr);
        }
        return false;
    }

    // Soil Moisture Experiment of 2002 (SMEX02)
    public static boolean isEquals(String full, String abbr) {
        String[] fullSplit = full.split(" ");
        String[] abbrSplit = abbr.split("");
        boolean bool = false;
        if (fullSplit.length > 1 && abbrSplit.length > 1) {
            String a = abbrSplit[1];
            String f = fullSplit[1];
            // 如果相同的第1位，首字母相等，直接true，如果不相等，但如果全写词组的第1位f（即第二个单词）是介词，那么，全写词组的下一个单词是a开头也可以，或者，全写词组的上一位包含也可以。若都不行，则bool仍然为false，下面的就无法进行了，最终将返回false。
            if (f.startsWith(a)) {
                bool = true;
            } else if (("a".equals(f) || "an".equals(f) || "of".equals(f) || "in".equals(f) || "on".equals(f) || "the".equals(f) || "and".equals(f) || "is".equals(f) || "at".equals(f) || "for".equals(f) || "with".equals(f)
                    || "by".equals(f) || "from".equals(f) || "to".equals(f) || "between".equals(f) || "among".equals(f) || "under".equals(f) || "above".equals(f) || "voor".equals(f))) {
                if (fullSplit.length > 2 && fullSplit[2].startsWith(a)) {
                    bool = true;
                } else if (fullSplit[0].contains(a)) {
                    bool = true;
                }
            }
        }
        if (bool) {
            for (String s : abbrSplit) {
                if (!full.contains(s)) {
                    return false;
                }
            }
        }
        return bool;
    }

    // 关键词的相关信息，包含：表自增主键的集合，集合的总数量以及关键词。
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class KeyWordSumAids {
        private List<Integer> aidList = new ArrayList<>();
        private int sum = 0;
        private String keyWord = "";
    }

    // 研究热点输出给前端的实体类，包含柱状图的最大值和关键词相关信息
    @Data
    public static class ResearchHotspotsInfo {
        private int max = 0;
        private List<KeyWordSumAids> keyWordAidList = new ArrayList<>();
    }

}
